package com.xmall.order.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xmall.define.base.Result;
import com.xmall.define.base.ResultCode;
import com.xmall.define.dict.*;
import com.xmall.define.dto.login.MemberDto;
import com.xmall.define.dto.member.DeliveryAddressDto;
import com.xmall.define.dto.order.OrderDto;
import com.xmall.order.define.dto.OrderDtoWithCascade;
import com.xmall.order.define.dto.OrderItemDto;
import com.xmall.define.dto.pay.AlipayTradeDto;
import com.xmall.define.dto.product.SkuInfoDto;
import com.xmall.define.dto.product.SkuQuantity;
import com.xmall.define.request.pay.OrderPaySuccessRequest;
import com.xmall.exception.XmallException;
import com.xmall.order.define.dict.AfterSaleStatusEnum;
import com.xmall.order.define.dto.OrderConfirmResponseDto;
import com.xmall.order.define.dto.OrderDo;
import com.xmall.order.define.dto.OrderQueryParams;
import com.xmall.order.define.request.api.OrderConfirmRequest;
import com.xmall.order.define.request.api.OrderSubmitRequest;
import com.xmall.order.entity.Order;
import com.xmall.order.entity.OrderDelivery;
import com.xmall.order.entity.OrderItem;
import com.xmall.order.entity.OrderPaySuccessRetryLog;
import com.xmall.order.mapper.OrderMapper;
import com.xmall.order.service.*;
import com.xmall.order.service.other.IAmountCalculateService;
import com.xmall.order.service.other.IMsgService;
import com.xmall.order.service.other.IOrderTradeService;
import com.xmall.order.service.other.IRemoteService;
import com.xmall.order.service.remote.PayServiceFeign;
import com.xmall.order.service.remote.ProductServiceFeign;
import com.xmall.order.utils.ConvertUtils;
import com.xmall.define.request.product.StockLockRequest;
import com.xmall.order.utils.AmountUtils;
import com.xmall.utils.XmallUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 服务实现类
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Resource
    private IOrderItemService orderItemService;

    @Resource
    private IRemoteService remoteService;

    @Resource
    private IMsgService msgService;

    @Resource
    private IOrderCancelRetryLogService orderCancelRetryLogService;

    @Resource
    private ProductServiceFeign productServiceFeign;

    @Resource
    private IOrderPaySuccessRetryLogService orderPaySuccessRetryLogService;

    @Resource
    private IOrderTradeService orderTradeService;

    @Resource
    private IOrderDeliveryService orderDeliveryService;

    @Resource
    private IAmountCalculateService amountCalculateService;

    @Resource
    private PayServiceFeign payServiceFeign;

    /**
     * 库存检查
     */
    @Override
    public List<Long> stockCheck(List<SkuQuantity> skuQuantityList) {
        Result<List<Long>> result = productServiceFeign.stockCheck(skuQuantityList);
        XmallUtils.checkRemoteResult(result);
        return result.getData();
    }

    /**
     * 订单确认
     */
    @Override
    public OrderConfirmResponseDto confirm(OrderConfirmRequest request) {
        OrderConfirmResponseDto response = new OrderConfirmResponseDto();
        List<SkuQuantity> skuQuantityList = request.getSkuQuantityList();

        // sku列表信息
        List<SkuInfoDto> remoteSkuInfoList = remoteService.skuInfoList(skuQuantityList);

        // 地址信息
        response.setAddressId(request.getAddressId());

        // 商品子项
        List<OrderItemDto> orderItemDtoList = assemblyOrderItemList(skuQuantityList, remoteSkuInfoList);
        response.setOrderItemList(orderItemDtoList);

        // 商品数量
        long sum = orderItemDtoList.stream().collect(Collectors.summarizingInt(OrderItemDto::getQuantity)).getSum();
        response.setQuantity(Long.valueOf(sum).intValue());

        // 金额信息
        BigDecimal productAmount = amountCalculateService.calculateProductAmount(orderItemDtoList);
        BigDecimal discountAmount = amountCalculateService.calculateDiscountAmount();
        BigDecimal deliveryAmount = amountCalculateService.calculateDeliveryAmount();
        response.setProductAmount(productAmount);
        response.setDiscountAmount(discountAmount);
        response.setDeliveryAmount(deliveryAmount);
        response.setPayAmount(productAmount.subtract(discountAmount).add(deliveryAmount));
        return response;
    }

    /**
     * 组装订单子项列表信息
     */
    private List<OrderItemDto> assemblyOrderItemList(List<SkuQuantity> skuQuantityList, List<SkuInfoDto> skuInfoList) {
        // sku数量信息集合(key:skuID, value:quantity)
        Map<Long, Integer> skuQuantityMap = skuQuantityList.stream().collect(Collectors.toMap(SkuQuantity::getSkuId, skuQuantity -> skuQuantity.getQuantity()));
        return skuInfoList.stream().map(sku -> {
            OrderItemDto dto = new OrderItemDto();
            dto.setSkuId(sku.getId());
            dto.setSkuTitle(sku.getTitle());
            dto.setSkuImage(sku.getImage());
            dto.setPrice(sku.getPrice());
            dto.setQuantity(skuQuantityMap.get(sku.getId()));
            dto.setTotalAmount(AmountUtils.skuAmount(sku.getPrice(), skuQuantityMap.get(sku.getId())));
            // sku属性组合
            List<String> skuAttrValueList = sku.getSkuAttrList().stream().map(skuAttr -> skuAttr.getAttrValue()).collect(Collectors.toList());
            dto.setSkuAttr(CollectionUtil.join(skuAttrValueList, ","));
            return dto;
        }).collect(Collectors.toList());
    }

    /**
     * 提交订单
     */
    @Transactional
    @Override
    public Long submit(OrderSubmitRequest request) {
        List<SkuQuantity> skuQuantityList = request.getSkuQuantityList();

        // 校验购物车信息
        if(request.getCartMode() && CollectionUtil.isEmpty(request.getCartIds())) {
            log.error("submitOrder: cartMode, cartIds is empty");
            throw new XmallException(ResultCode.ORDER_CART_LIST_EMPTY_ERROR);
        }

        // 库存检查
        List<Long> stockNotEnoughSkuIdList = this.stockCheck(skuQuantityList);
        if(CollectionUtil.isNotEmpty(stockNotEnoughSkuIdList)) {
            throw new XmallException(ResultCode.PRODUCT_STOCK_NOT_ENOUGH, stockNotEnoughSkuIdList);
        }

        // sku列表信息
        List<SkuInfoDto> remoteSkuInfoList = remoteService.skuInfoList(skuQuantityList);

        // 商品子项
        List<OrderItemDto> orderItemDtoList = assemblyOrderItemList(skuQuantityList, remoteSkuInfoList);

        // 金额信息
        BigDecimal productAmount = amountCalculateService.calculateProductAmount(orderItemDtoList);
        BigDecimal discountAmount = amountCalculateService.calculateDiscountAmount();
        BigDecimal deliveryAmount = amountCalculateService.calculateDeliveryAmount();
        BigDecimal payAmount = productAmount.subtract(discountAmount).add(deliveryAmount);

        // 保存订单
        MemberDto member = remoteService.memberInfo(request.getMemberId());
        Snowflake snowflake = IdUtil.getSnowflake(1, 1);
        Order order = new Order();
        order.setMemberId(request.getMemberId());
        order.setMemberName(member.getNickname());
        order.setOrderSn(snowflake.nextId());
        order.setOrderStatus(OrderStatusEnum.WAIT_PAY.getStatus());
        order.setProductAmount(productAmount);
        order.setDiscountAmount(discountAmount);
        order.setDeliveryAmount(deliveryAmount);
        order.setPayAmount(payAmount);
        this.save(order);

        // 保存订单子项
        List<OrderItem> orderItemList = orderItemDtoList.stream().map(orderItemDto -> {
            OrderItem orderItem = ConvertUtils.OrderItemModule.INSTANCE.toEntity(orderItemDto);
            orderItem.setOrderId(order.getId());
            return orderItem;
        }).collect(Collectors.toList());
        orderItemService.saveBatch(orderItemList);

        // 保存收货地址
        DeliveryAddressDto addressInfo = remoteService.deliveryAddress(request.getAddressId());
        OrderDelivery orderDelivery = ConvertUtils.DeliveryModule.INSTANCE.toDelivery(addressInfo);
        orderDelivery.setId(null);
        orderDelivery.setOrderId(order.getId());
        orderDeliveryService.save(orderDelivery);

        // 锁定库存
        StockLockRequest stockLockRequest = new StockLockRequest();
        stockLockRequest.setOrderId(order.getId());
        stockLockRequest.setSkuQuantityList(request.getSkuQuantityList());
        Result remoteStockDeductResult = productServiceFeign.stockLock(stockLockRequest);
        if(!remoteStockDeductResult.callSuccess()) {
            log.error("submitOrder: stock lock remote request is failed");
            throw new XmallException(ResultCode.PRODUCT_STOCK_NOT_ENOUGH);
        }

        // 发送订单创建消息
        msgService.sendOrderCreateMsg(order.getId(), request.getCartIds());
        return order.getId();
    }

    /**
     * 订单支付检查
     */
    @Override
    public void payCheck(Long orderId) {
        Order order = this.getById(orderId);
        XmallUtils.notNull(order);

        // 查询到订单显示未支付，但实际已经支付
        if(OrderStatusEnum.WAIT_PAY.getStatus() == order.getOrderStatus()) {
            Result<AlipayTradeDto> tradeResult = payServiceFeign.tradeInfo(order.getOrderSn().toString(), order.getTradeNo());
            if(tradeResult.callSuccess() && tradeResult.getData().getExists()
                && TradeStatusEnum.TRADE_SUCCESS.getValue().equalsIgnoreCase(tradeResult.getData().getTradeStatus())) {

                log.info("order pay check: orderId {}, show wait pay, but actually pay", orderId);
                OrderPaySuccessRequest request = new OrderPaySuccessRequest();
                request.setOrderId(orderId);
                request.setOrderSn(order.getOrderSn().toString());
                request.setTradeNo(tradeResult.getData().getTradeNo());
                request.setPayType(PayTypeEnum.ALI_PAY.getType());
                request.setPayPlatform(PayPlatformEnum.MOBILE_H5.getType());
                request.setPayTime(new Date());
                msgService.sendPaySuccessMsg(request);
            }
        }

    }

    /**
     * 更新订单状态
     */
    @Override
    public void updateOrderStatus(Long orderId, OrderStatusEnum orderStatusEnum) {
        Order order = this.getById(orderId);
        XmallUtils.notNull(order);

        Order updateOrder = new Order();
        updateOrder.setId(order.getId());
        updateOrder.setOrderStatus(orderStatusEnum.getStatus());
        this.updateById(updateOrder);
    }

    /**
     * 更新订单状态
     */
    @Override
    public void updateOrderStatusStrict(Long orderId, OrderStatusEnum preOrderStatusEnum, OrderStatusEnum newOrderStatusEnum) {
        Order updateEntity = new Order();
        updateEntity.setId(orderId);
        updateEntity.setOrderStatus(newOrderStatusEnum.getStatus());

        if(OrderStatusEnum.CANCELED.getStatus() == newOrderStatusEnum.getStatus()) {
            updateEntity.setCancelTime(new Date());
        }
        if(OrderStatusEnum.WAIT_RECEIVE.getStatus() == newOrderStatusEnum.getStatus()) {
            updateEntity.setDeliveryTime(new Date());
        }
        if(OrderStatusEnum.FINISHED.getStatus() == newOrderStatusEnum.getStatus()) {
            updateEntity.setFinishTime(new Date());
        }

        boolean executeResult = this.update(updateEntity, new QueryWrapper<Order>().eq("id", orderId).eq("order_status", preOrderStatusEnum.getStatus()));
        if(!executeResult) {
            throw new XmallException(ResultCode.ORDER_UPDATE_STATUS_ERROR);
        }
    }

    /**
     * 取消订单
     */
    @Transactional
    @Override
    public void cancelOrder(Long id) {
        if(orderCancelRetryLogService.complete(id) && orderTradeService.shouldExecuteCancel(id)) {
            this.updateOrderStatusStrict(id, OrderStatusEnum.WAIT_PAY, OrderStatusEnum.CANCELED);
        }
    }

    /**
     * 支付成功
     */
    @Transactional
    @Override
    public void paySuccess(OrderPaySuccessRetryLog log) {
        if(orderPaySuccessRetryLogService.complete(log.getOrderId())) {
            Order updateEntity = new Order();
            updateEntity.setId(log.getOrderId());
            updateEntity.setOrderStatus(OrderStatusEnum.WAIT_CONSIGN.getStatus());
            updateEntity.setTradeNo(log.getTradeNo());
            updateEntity.setPayType(log.getPayType());
            updateEntity.setPayPlatform(log.getPayPlatform());
            updateEntity.setPayTime(log.getPayTime());

            boolean executeResult = this.update(updateEntity, new QueryWrapper<Order>().eq("id", log.getOrderId()).eq("order_status", OrderStatusEnum.WAIT_PAY.getStatus()));
            if(!executeResult) {
                throw new XmallException(ResultCode.ORDER_UPDATE_STATUS_ERROR);
            }

            // 设置订单售后状态
            this.setOrderAfterSaleStatus(log.getOrderId(), AfterSaleStatusEnum.VALID);
            this.setOrderItemAfterSaleStatusByOrderId(log.getOrderId(), AfterSaleStatusEnum.UN_VALID);
        }
    }

    /**
     * 确认收货
     */
    @Override
    public void takeDelivery(Long id) {
        this.updateOrderStatusStrict(id, OrderStatusEnum.WAIT_RECEIVE, OrderStatusEnum.FINISHED);
    }

    /**
     * 删除订单
     */
    @Override
    public void deleteOrder(Long id) {

    }

    /**
     * 设置订单售后状态
     */
    @Override
    public void setOrderAfterSaleStatus(Long orderId, AfterSaleStatusEnum afterSaleStatusEnum) {
        Order updateOrder = new Order();
        updateOrder.setId(orderId);
        updateOrder.setAfterSaleStatus(afterSaleStatusEnum.getStatus());
        this.updateById(updateOrder);
    }

    /**
     * 设置订单子项售后状态
     */
    @Override
    public void setOrderItemAfterSaleStatusByOrderId(Long orderId, AfterSaleStatusEnum afterSaleStatusEnum) {
        List<OrderItem> updateOrderItemList = orderItemService.list(new QueryWrapper<OrderItem>().eq("order_id", orderId)).stream().map(orderItem -> {
            OrderItem updateOrderItem = new OrderItem();
            updateOrderItem.setId(orderItem.getId());
            updateOrderItem.setAfterSaleStatus(afterSaleStatusEnum.getStatus());
            return updateOrderItem;
        }).collect(Collectors.toList());
        orderItemService.updateBatchById(updateOrderItemList);
    }

    /**
     * 设置订单子项售后状态
     */
    @Override
    public void setOrderItemAfterSaleStatus(Long orderItemId, AfterSaleStatusEnum afterSaleStatusEnum) {
        OrderItem updateOrderItem = new OrderItem();
        updateOrderItem.setId(orderItemId);
        updateOrderItem.setAfterSaleStatus(afterSaleStatusEnum.getStatus());
        orderItemService.updateById(updateOrderItem);
    }

    /**
     * 详情
     */
    @Override
    public OrderDto info(Long id) {
        Order entity = this.getById(id);
        XmallUtils.notNull(entity);

        return ConvertUtils.OrderModule.INSTANCE.toDto(entity);
    }

    /**
     * 详情
     */
    @Override
    public OrderDtoWithCascade infoWithCascade(Long id) {
        OrderDo entity = this.baseMapper.info(id);
        XmallUtils.notNull(entity);

        return ConvertUtils.OrderModule.INSTANCE.doToDto(entity);
    }

    /**
     * 自定义分页查询
     */
    @Override
    public Page<OrderDtoWithCascade> listPageWithCascade(Page page, OrderQueryParams query) {
        Page<OrderDtoWithCascade> result = new Page<>();
        Page<OrderDo> listPage = this.baseMapper.listPage(page, query);

        BeanUtils.copyProperties(listPage, result);
        result.setRecords(ConvertUtils.OrderModule.INSTANCE.doListToDtoList(page.getRecords()));
        return result;
    }

}
